package com.yourenbang.lingyun.transaction.web.service.impl.fund.fundopt;

import com.yourenbang.lingyun.constants.BizCodeMessages;
import com.yourenbang.lingyun.core.message.Result;
import com.yourenbang.lingyun.core.util.BizAssertUtils;
import com.yourenbang.lingyun.core.util.KLog;
import com.yourenbang.lingyun.core.util.StringUtils;
import com.yourenbang.lingyun.domain.FundAccount;
import com.yourenbang.lingyun.transaction.constant.ChangePlanStatus;
import com.yourenbang.lingyun.transaction.constant.JournalStatus;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountChangePlanMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountJournalMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountMapper;
import com.yourenbang.lingyun.transaction.web.domain.dto.ChangeFundAccountV2DTO;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountChangePlan;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountJournal;
import com.yourenbang.lingyun.transaction.web.service.fund.FundAccountChangePlanService;
import com.yourenbang.lingyun.transaction.web.service.fund.fundopt.FundOptSubtractService;
import com.yourenbang.transaction.api.model.dto.AddBillDTO;
import com.yourenbang.transaction.api.service.fund.FundBillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Objects;

@Slf4j
@Service
public class FundOptSubtractServiceImpl implements FundOptSubtractService {

    @Autowired
    private TransactionFundAccountMapper transactionFundAccountMapper;
    @Resource
    private TransactionFundAccountChangePlanMapper fundAccountChangePlanMapper;
    @Resource
    private FundAccountChangePlanService fundAccountChangePlanService;

    @Resource
    private FundBillService fundBillService;
    @Resource
    private TransactionFundAccountJournalMapper fundAccountJournalMapper;

    @Transactional
    @Override
    public Result subtract(FundAccount fromFundAccount, FundAccount toFundAccount,
                           ChangeFundAccountV2DTO changeFundAccountV2DTO) {

        KLog.info("[通用账户提现减账]入参:{}", changeFundAccountV2DTO.toString());

        FundAccountChangePlan changePlan = fundAccountChangePlanMapper.getByOrderId(fromFundAccount.getPlatNo(),
                changeFundAccountV2DTO.getOrderNo());

        //提现账户减, 用户账户可提- 已提+
        //可提不够 返回失败

        //完善减账参数属性
//        perfectSubtractChangeFundAccountDTO(changeFundAccountDTO);
//        KLog.info("[转化后通用账户减账]入参:{}", changeFundAccountDTO.toString());


        if (fromFundAccount.getAccountAmountWithdrawable().compareTo(changeFundAccountV2DTO.getChangeAmount()) < 0) {
            return Result.builder().fail(BizCodeMessages.TRANSACTION_SUBTRACT_ACCOUNT_FAIL, "系统提现余额不足");
        }

        if (toFundAccount.getAccountAmountWithdrawable().compareTo(changeFundAccountV2DTO.getChangeAmount()) < 0) {
            return Result.builder().fail(BizCodeMessages.TRANSACTION_SUBTRACT_ACCOUNT_FAIL, "用户可提余额不足");
        }

        try {

            //资金发起方 可提资金减少
            int i = transactionFundAccountMapper.withdrawableSubtract(fromFundAccount.getFundAccountId(),
                    changeFundAccountV2DTO.getChangeAmount());
            BizAssertUtils.assertOne(i, BizCodeMessages.TRANSACTION_SUBTRACT_ACCOUNT_FAIL, "系统可提余额不足");

            //记录资金发起方流水
            FundAccountJournal journal = getFromFundAccountJournal(changeFundAccountV2DTO, fromFundAccount, changePlan);
            fundAccountJournalMapper.insert(journal);

            //资金接收方 可提资金增加
            int i1 = transactionFundAccountMapper.withdrawableSubtract(toFundAccount.getFundAccountId(), changeFundAccountV2DTO.getChangeAmount());
            BizAssertUtils.assertOne(i1, BizCodeMessages.TRANSACTION_SUBTRACT_ACCOUNT_FAIL, "用户可提余额不足");

            //记录资金接收起方流水
            FundAccountJournal tojournal = getToFundAccountJournal(changeFundAccountV2DTO, toFundAccount, changePlan);
            fundAccountJournalMapper.insert(tojournal);

            KLog.info("[通用减账结束]:入参{}", changeFundAccountV2DTO.toString());
            changePlan.setAccountChangePlanStatus(ChangePlanStatus.CHANGED);
            fundAccountChangePlanMapper.updateById(changePlan);

            //账单
            addBill(changeFundAccountV2DTO, toFundAccount, changePlan);

        } catch (Exception e) {

            //提现减账失败时,金额放到冻结,
            KLog.error("通用减账错误:{},请求参数:{}", e.getMessage(), changeFundAccountV2DTO, e);

            //抓到异常,更新plan表为wait to change
            changePlan.setAccountChangePlanStatus(ChangePlanStatus.FAIL);
            fundAccountChangePlanService.updateFundAccountChangePlan(changePlan);

            return Result.builder().fail(BizCodeMessages.TRANSACTION_SUBTRACT_ACCOUNT_FAIL);

        }


        return Result.builder().success();
    }

    //TODO 重复插入问题、能否抽象?
    private void addBill(ChangeFundAccountV2DTO changeFundAccountV2DTO, FundAccount userAccountByType,
                         FundAccountChangePlan changePlan) {
        //账单
        AddBillDTO billDTO = AddBillDTO.builder()
                .userBaseId(changeFundAccountV2DTO.getUserBaseId())
                .fundAccountId(userAccountByType.getFundAccountId())
                .fundAccountChangePlanId(changePlan.getFundAccountChangePlanId())
                .orderId(changeFundAccountV2DTO.getOrderNo())
                .amount(changeFundAccountV2DTO.getChangeAmount().negate())
                .billType(changeFundAccountV2DTO.getFundBillType())
                .channelId(changeFundAccountV2DTO.getChannelId())
                .detail(changeFundAccountV2DTO.getDetail())
                .showFlag(changeFundAccountV2DTO.getShowFlag())
                .platNo(changeFundAccountV2DTO.getPlatNo())
                .build();

        //手动操作
        if (StringUtils.isNotBlank(changeFundAccountV2DTO.getOperator())
                && Objects.nonNull(changeFundAccountV2DTO.getManualFlag())
                && changeFundAccountV2DTO.getManualFlag()) {
            billDTO.setOperator(changeFundAccountV2DTO.getOperator());
            billDTO.setManualFlag(changeFundAccountV2DTO.getManualFlag());
        }


        billDTO.setAccountMainJournalType(changePlan.getAccountMainJournalType());
        billDTO.setAccountMainJournalTypeName(changeFundAccountV2DTO.getJournalInfo().getAccountMainJournalType().getName());

        fundBillService.addBill(billDTO);
    }

    private FundAccountJournal getFromFundAccountJournal(ChangeFundAccountV2DTO changeFundAccountV2DTO, FundAccount sysAccount,
                                                         FundAccountChangePlan changePlan) {
        //变动之前可提现金额
        BigDecimal accountAmountWithdrawableBefore = sysAccount.getAccountAmountWithdrawable();
        //计算变动之后可提现金额
        BigDecimal accountAmountWithdrawableAfter =
                accountAmountWithdrawableBefore.subtract(changeFundAccountV2DTO.getChangeAmount());

        //变动之前已提现金额 发起方已提现金额增加
        BigDecimal accountAmountWithdrawedBefore = sysAccount.getAccountAmountWithdrawed();
        //计算变动后的已提现金额
        BigDecimal accountAmountWithdrawedAfter = accountAmountWithdrawedBefore.add(changeFundAccountV2DTO.getChangeAmount());


        FundAccountJournal journal = new FundAccountJournal(changeFundAccountV2DTO.getPlatNo());
        journal.setChannelId(changeFundAccountV2DTO.getChannelId());
        journal.setFundAccountChangePlanId(changePlan.getFundAccountChangePlanId());
        journal.setFundAccountId(sysAccount.getFundAccountId());
        journal.setJournalStatus(JournalStatus.SUCCESS);
        journal.setJournalChangeValue(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountJournalAcid(changePlan.getOrderId());
        journal.setAccountMainJournalType(changePlan.getAccountMainJournalType());
        journal.setAccountMainJournalTypeName(changeFundAccountV2DTO.getJournalInfo().getAccountMainJournalType().getName());


        //变动金额记录
        journal.setAccountIncome(BigDecimal.ZERO);
        journal.setAccountAmountFrozen(BigDecimal.ZERO);
        //可提现变动金额
        journal.setAccountAmountWithdrawable(changeFundAccountV2DTO.getChangeAmount());
        //已提现变动金额
        journal.setAccountAmountWithdrawed(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountAmountUndepositable(BigDecimal.ZERO);


        //变动前后记录
        //加帐情况,系统income收入无变化
        journal.setAccountIncomeBefore(sysAccount.getAccountIncome());
        journal.setAccountIncomeAfterwards(sysAccount.getAccountIncome());

        journal.setAccountAmountFrozenBefore(sysAccount.getAccountAmountFrozen());
        journal.setAccountAmountFrozenAfterwards(sysAccount.getAccountAmountFrozen());

        journal.setAccountAmountWithdrawableBefore(accountAmountWithdrawableBefore);
        journal.setAccountAmountWithdrawableAfterwards(accountAmountWithdrawableAfter);

        journal.setAccountAmountWithdrawedBefore(accountAmountWithdrawedBefore);
        journal.setAccountAmountWithdrawedAfterwards(accountAmountWithdrawedAfter);

        journal.setAccountAmountUndepositableBefore(sysAccount.getAccountAmountUndepositable());
        journal.setAccountAmountUndepositableAfterwards(sysAccount.getAccountAmountUndepositable());

        return journal;
    }

    private FundAccountJournal getToFundAccountJournal(ChangeFundAccountV2DTO changeFundAccountV2DTO, FundAccount userAccountByType,
                                                       FundAccountChangePlan changePlan) {

        BigDecimal toAccountAmountWithdrawableBefore = userAccountByType.getAccountAmountWithdrawable();

        //变动之后可提现金额
        BigDecimal toAccountAmountWithdrawableAfter = toAccountAmountWithdrawableBefore
                .subtract(changeFundAccountV2DTO.getChangeAmount());

        //已提加 可提减
        FundAccountJournal journal = new FundAccountJournal(changeFundAccountV2DTO.getPlatNo());
        journal.setChannelId(changeFundAccountV2DTO.getChannelId());
        journal.setFundAccountChangePlanId(changePlan.getFundAccountChangePlanId());
        journal.setFundAccountId(userAccountByType.getFundAccountId());
        journal.setJournalStatus(JournalStatus.SUCCESS);
        journal.setJournalChangeValue(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountJournalAcid(changePlan.getOrderId());

        journal.setAccountMainJournalType(changePlan.getAccountMainJournalType());
        journal.setAccountMainJournalTypeName(changeFundAccountV2DTO.getJournalInfo().getAccountMainJournalType().getName());

        journal.setAccountIncome(BigDecimal.ZERO);
        journal.setAccountAmountFrozen(BigDecimal.ZERO);
        journal.setAccountAmountWithdrawable(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountAmountWithdrawed(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountAmountUndepositable(BigDecimal.ZERO);

        journal.setAccountIncomeBefore(userAccountByType.getAccountIncome());
        journal.setAccountIncomeAfterwards(userAccountByType.getAccountIncome());

        journal.setAccountAmountFrozenBefore(userAccountByType.getAccountAmountFrozen());
        journal.setAccountAmountFrozenAfterwards(userAccountByType.getAccountAmountFrozen());

        //变动之前可提现金额 接收方可提现金额增加
        journal.setAccountAmountWithdrawableBefore(toAccountAmountWithdrawableBefore);
        journal.setAccountAmountWithdrawableAfterwards(toAccountAmountWithdrawableAfter);

        journal.setAccountAmountWithdrawedBefore(userAccountByType.getAccountAmountWithdrawed());
        journal.setAccountAmountWithdrawedAfterwards(userAccountByType.getAccountAmountWithdrawed()
                .add(changeFundAccountV2DTO.getChangeAmount()));

        journal.setAccountAmountUndepositableBefore(userAccountByType.getAccountAmountUndepositable());
        journal.setAccountAmountUndepositableAfterwards(userAccountByType.getAccountAmountUndepositable());


        return journal;
    }

}
